﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public struct ExternalResourcePackageFileInfo
{
    public string fileName;
    public int originalLength;
    public int begin;
    public int length;

    public void Write(System.IO.BinaryWriter writer)
    {
        writer.Write(fileName);
        writer.Write(originalLength);
        writer.Write(begin);
        writer.Write(length);
    }

    public void Read(System.IO.BinaryReader reader)
    {
        fileName = reader.ReadString();
        originalLength = reader.ReadInt32();
        begin = reader.ReadInt32();
        length = reader.ReadInt32();
    }
}

public class ExternalResourcePackageInfo
{
    public byte compressType;
    public ExternalResourcePackageFileInfo[] fileInfos;

    public void Write(System.IO.BinaryWriter writer)
    {
        writer.Write(compressType);
        writer.Write((int)fileInfos.Length);

        for (int i = 0; i < fileInfos.Length; i++)
        {
            fileInfos[i].Write(writer);
        }
    }

    public void Read(System.IO.BinaryReader reader)
    {
        compressType = reader.ReadByte();
        int len = reader.ReadInt32();

        fileInfos = new ExternalResourcePackageFileInfo[len];
        for (int i = 0; i < len; i++)
        {
            fileInfos[i].Read(reader);
        }
    }
}

public static class ExternalResourceManager
{
    static readonly string dirPath = CommonUtil.cachePath + "/external_resource";

    static object downloadObj;
    static System.Action<bool> callback;

    static int version;
    static string md5;
    static string packageFilePath;
    static System.IO.FileStream fileStream;
    static ExternalResourcePackageInfo packageInfo;
    static Dictionary<string, ExternalResourcePackageFileInfo> fileInfoMap = new Dictionary<string, ExternalResourcePackageFileInfo>();

    static ExternalResourceManager()
    {
        CommonUtil.CreateDir(dirPath);
        Init();
    }

    static bool Init()
    {
        if(!SwitchConfig.isEnableHotFix)
        {
            return true;
        }
        
        if (packageFilePath != null)
        {
            return false;
        }

        string[] paths = System.IO.Directory.GetFiles(dirPath, "*", System.IO.SearchOption.TopDirectoryOnly);
        for(int i = 0; i < paths.Length; i++)
        {
            string tempPath = paths[i];

            string fileName = System.IO.Path.GetFileNameWithoutExtension(tempPath);
            if(fileName.StartsWith("."))
            {
                continue;
            }

            packageFilePath = tempPath;

            string[] s = fileName.Split('_');
            if (s.Length == 3)
            {
                version = System.Int32.Parse(s[0]);
                md5 = s[1];
                long size = System.Int64.Parse(s[2]);

                if (CommonUtil.GetFileSize(packageFilePath) != size)
                {
                    CommonUtil.Delete(packageFilePath);

                    version = 0;
                    md5 = null;
                    packageFilePath = null;

                    return false;
                }
            }
            else if (s.Length == 2)
            {
                version = System.Int32.Parse(s[0]);
                md5 = s[1];
            }
            else if(s.Length == 1)
            {
                version = 0;
                md5 = s[0];
            }

            return true;
        }

        return false;
    }

    static void InitFileInfos()
    {
        if (fileStream != null || packageInfo != null)
        {
            return;
        }

        if (string.IsNullOrEmpty(packageFilePath))
        {
            return;
        }

        fileStream = new System.IO.FileStream(packageFilePath, System.IO.FileMode.Open);
        packageInfo = new ExternalResourcePackageInfo();
        System.IO.BinaryReader reader = new System.IO.BinaryReader(fileStream);
        packageInfo.Read(reader);

        for (int i = 0; i < packageInfo.fileInfos.Length; i++)
        {
            ExternalResourcePackageFileInfo fileInfo = packageInfo.fileInfos[i];
            if (fileInfoMap.ContainsKey(fileInfo.fileName))
            {
                Debug.LogError("repeat file path: " + fileInfo.fileName);
            }
            fileInfoMap.Add(fileInfo.fileName, fileInfo);
        }
    }

    public static string GetMd5()
    {
        return md5;
    }

    public static bool Download(string url, uint size, System.Action<bool> _callback)
    {
        if (string.IsNullOrEmpty(url) || size <= 0 || downloadObj != null || callback != null || _callback == null)
        {
            return false;
        }

        string fileName = System.IO.Path.GetFileName(url);
        string saveFilePath = System.IO.Path.Combine(dirPath, fileName);

        if (saveFilePath == packageFilePath)
        {
            _callback(true);
            return true;
        }

        callback = _callback;

        CommonUtil.Delete(packageFilePath);
        packageFilePath = null;
        version = 0;
        md5 = null;
        packageInfo = null;
        fileInfoMap.Clear();
        if (fileStream != null)
        {
            fileStream.Close();
            fileStream = null;
        }

        downloadObj = null;


        downloadObj = HttpDownloadManager.instance.Download(url, null, size, size, CompressType.NONE, saveFilePath, null, OnDownloadComplete);
        return true;
    }

    public static int GetProgress()
    {
        return HttpDownloadManager.instance.getRequestProgress(downloadObj);
    }

    public static int GetVersion()
    {
        return version;
    }

    static void OnDownloadComplete(string url, string localPath, DownloadError status)
    {
        var _callback = callback;
        callback = null;
        downloadObj = null;
        if (status != DownloadError.None)
        {
            _callback(false);
        }
        else
        {
            _callback(Init());
        }
    }

    public static byte[] GetBytes(string path)
    {
        InitFileInfos();

        if (fileStream == null || packageInfo == null)
        {
            return null;
        }

        ExternalResourcePackageFileInfo fileInfo = new ExternalResourcePackageFileInfo();
        if (!fileInfoMap.TryGetValue(path, out fileInfo))
        {
            return null;
        }

        if(fileInfo.originalLength <= 0)
        {
            throw new System.Exception("file originalLength <=0: " + path);
        }

        long oldPos = fileStream.Position;

        fileStream.Position = fileStream.Position + fileInfo.begin;
        byte[] compressedData = new byte[fileInfo.length];
        fileStream.Read(compressedData, 0, compressedData.Length);

        byte[] data = null;
        if (!CompressManager.DecompressBytes(packageInfo.compressType, compressedData, fileInfo.originalLength, out data))
        {
            throw new System.Exception("decompress failed: " + path + "   " + packageInfo.compressType);
        }

        fileStream.Position = oldPos;

        return data;
    }

	public static string GetString(string path)
	{
		byte[] data = GetBytes(path);
		if (data == null)
		{
			return null;
		}

		return System.Text.Encoding.UTF8.GetString (data);
	}
}
